#ifndef _OPTIMIZER_H_
#define _OPTIMIZER_H_

#include <vector>
#include <algorithm>

#include <boost/math/constants/constants.hpp>

#ifdef _CILK_
#include <cilk/cilk.h>
#endif

#include <CGAL/Timer.h>

#include <Geex/basics/types.h>

#include "knitro.h"

#include "../common/polytope.h"
#include "triangulation.h"
#include "containment.h"


namespace Packing
{
	using namespace Packing_common;

class Optimizer
{
	typedef enum {SUCCESS, FAIL} Optimization_res;
	typedef enum {TRANSLATION_CONSTRAINT, ROTATION_CONSTRAINT, SCALING_CONSTRAINT} Constraint_type;

public:

	Optimizer(std::vector<Packing_polygon*>& polygons, Polygon_triangulation& polygon_triangulation);
	~Optimizer();

	void run_one_lloyd(bool enlarge);

	unsigned int nb_polytopes() const { return polygon_set.size(); }

private:

	void transform_one_polytope(unsigned int id, const Parameter& p);

	Optimization_res optimize_one_polytope(unsigned int id, bool enlarge, Parameter& solution);

	bool constrain_transformation(Constraint_type type, unsigned int id, Parameter& p);

	static int callback(const int evalRequestCode, const int n, const int m, const int nnzJ, 
						const int nnzH,	const double * const x,	const double * const lambda, 
						double * const obj, double * const c, double * const objGrad, double * const jac,
						double * const hessian,	double * const hessVector, void * userParams);
	int knitro_optimize(double* io_k, double* io_theta, double* io_t1, double* io_t2, unsigned int idx);

private:

	static Optimizer *instance;

	std::vector<Packing_polygon*>& polygon_set;
	Polygon_triangulation& pt;
	
	double rot_lower_bound;
	double rot_upper_bound;

#ifdef _CILK_
	std::vector<Containment> containments;
#else
	Containment containment;
#endif // _CILK_
};

}
#endif